package com.streamhub.client;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.json.client.JSONObject;

/**
 * The main class for connecting to a StreamHub Push Server and subscribing to topics.
 * <p>
 * Example of the adapter in use:
<pre>
StreamHubGWTAdapter hub = new StreamHubGWTAdapter();
hub.connect("http://push.example.com/");
hub.subscribe("GOOG", updateListener);
hub.unsubscribe("GOOG");
hub.disconnect(); 
</pre>
 * <p>
 * The source code for the adapter and examples is available at:  
 * <a href="http://code.google.com/p/gwt-comet-streamhub/">GWT Comet StreamHub on Google Code</a>.
 */
public class StreamHubGWTAdapter {
	private static List<StreamHubGWTUpdateListener> listeners = new ArrayList<StreamHubGWTUpdateListener>();
	private final StreamHubBridge bridge;
	
	/**
	 * This method is called internally everytime an update is 
	 * received to a subscribed topic.  It notifies any {@link StreamHubGWTUpdateListener} 
	 * that has been added via a call to {@link #subscribe(String, StreamHubGWTUpdateListener)}.  
	 * It is useful for testing without connecting to a real server.
	 * 
	 * @param topic		the topic on which the update was received
	 * @param update	the fields received in the update
	 */
	public static void onUpdate(String topic, JavaScriptObject update) {
		JSONObject jsonUpdate = new JSONObject(update);
		
		for (StreamHubGWTUpdateListener listener : listeners) {
			listener.onUpdate(topic, jsonUpdate);
		}
	}

	/**
	 * Creates a new un-connected StreamHub GWT adapter
	 */
	public StreamHubGWTAdapter() {
		this(new StreamHubGWTBridge());
	}
	
	StreamHubGWTAdapter(StreamHubBridge bridge) {
		this.bridge = bridge;
		bridge.createStreamHub();
	}
	
	/**
	 * Connects to the server
	 * 
	 * @param url	the URL of the server to connect to e.g. <code>http://localhost:7979/</code>.  
	 * 				The trailing slash is important.
	 */
	public void connect(String url) {
		bridge.exportStaticOnUpdateMethod();
		bridge.connectHub(url);
	}

	/**
	 * Connects to the server with advanced 
	 * failover configuration.
	 * 
	 * @param failoverParameters	the failover configuration parameters
	 * @see FailoverParameters
	 */
	public void connect(FailoverParameters failoverParameters) {
		bridge.exportStaticOnUpdateMethod();
		bridge.connectHub(failoverParameters.getJavaScriptObject());
	}

	/**
	 * Subscribes to a particular topic.  The {@link StreamHubGWTUpdateListener} will 
	 * be notified of any updates on this topic.
	 * 
	 * @param topic		the topic to subscribe to
	 * @param listener	the listener to receive updates on this topic
	 * @see #subscribe(String[], StreamHubGWTUpdateListener)
	 * @see StreamHubGWTUpdateListener
	 */
	public void subscribe(String topic, StreamHubGWTUpdateListener listener) {
		addListener(listener);
		bridge.subscribe(topic);
	}

	/**
	 * Subscribes to an array of topics in a single request to the StreamHub server.  
	 * This method should be preferred over {@link #subscribe(String, StreamHubGWTUpdateListener)} 
	 * where multiple topics are used as it reduces the number of HTTP requests involved.  The {@link StreamHubGWTUpdateListener} will 
	 * be notified of updates on any of the topics.
	 * 
	 * @param topics	the topics to subscribe to
	 * @param listener	the listener to receive updates on ay of the topics
	 * @see #subscribe(String, StreamHubGWTUpdateListener)
	 * @see StreamHubGWTUpdateListener
	 */
	public void subscribe(String[] topics, StreamHubGWTUpdateListener listener) {
		addListener(listener);
		bridge.subscribe(JavaScriptUtils.toArray(topics));
	}
	
	/**
	 * Unsubscribes from a particular topic
	 * 
	 * @param topic	the topic to unsubscribe from
	 */
	public void unsubscribe(String topic) {
		bridge.unsubscribeHub(topic);
	}
	
	/**
	 * Unsubscribes from an array of topics
	 * 
	 * @param topics	the topics to unsubscribe from
	 */
	public void unsubscribe(String[] topics) {
		bridge.unsubscribeHub(JavaScriptUtils.toArray(topics));
	}
	
	/**
	 * Publishes data on a particular topic.  The data contained in <code>jsonObject</code> will be 
     * sent back to the server.  The server may choose to publish this data to all 
     * subscribed clients or instead return a custom response to this client.
     * <p>
     * Example of publishing data:
<pre>
// Connect
StreamHubGWTAdapter hub = new StreamHubGWTAdapter();
hub.connect("http://push.example.com/");

// Create some data to publish
JSONObject payload = new JSONObject();
payload.put("Name", new JSONString("GWT-Fan"));
payload.put("Message", new JSONString("GWT is cool!"));

// Publish the data
hub.publish("ChatRoom", payload);
</pre>
 	 *
	 * @param topic			the topic to publish the data on
	 * @param jsonObject	the data to publish
	 */
	public void publish(String topic, JSONObject jsonObject) {
		bridge.publish(topic, jsonObject.toString());
	}
	
	/**
	 * Disconnects from the StreamHub server.  Note: this does not delete the subscriptions, use unsubscribe 
     * to remove the subscriptions.
	 */
	public void disconnect() {
		bridge.disconnectHub();
	}
	
	private void addListener(StreamHubGWTUpdateListener listener) {
		listeners.add(listener);
	}
}
